#ifndef TRANSMITTER_H
#define TRANSMITTER_H

#if defined(ARDUINO) && ARDUINO >= 100
	#include "Arduino.h"
#else
	#include "WProgram.h"
#endif

#include "nRF24L01.h"
#include "RF24.h"
#include "StarburstCommon.h"

class Transmitter
{
public:
  Transmitter(uint8_t cePin, uint8_t csPin) :radio(cePin, csPin) {
  }

  void init() {
      initRadio(radio, false);
      radio.setPayloadSize(DEFAULT_PACKET_SIZE);
      breakAudio();
  }


  void breakAudio() {
    memcpy(buffer,audioBreak,4);
    write();
  }

  void setAudioMode(uint8_t lowBrightness, uint8_t highBrightness, uint8_t saturation) {
    setLightMode(Sound, lowBrightness, highBrightness,saturation);
  }

  void setLoadingMode(uint8_t r, uint8_t g, uint8_t b) {
    setLightMode(Loading,r,g,b);
  }

  void setConstantColorMode(uint8_t hue, uint8_t saturation, uint8_t lum) {
    setLightMode(Solid,hue,saturation,lum);
  }

  void setStreamingMode(uint8_t bitdepth) {
//    setLightMode(LIGHT_MODE_STREAMED, bitdepth,0,0);
  }

  void setLightMode(Mode mode, uint8_t arg1, uint8_t arg2, uint8_t arg3) { 
    clearBuffer();
    ModeMessage msg(mode,arg1,arg2,arg3);
    msg.copyTo(buffer);
    write();
    currentMode = mode;
  }

  void setDeliveryEnsured(bool deliveryEnsured) {
    this->ensureDelivery = ensureDelivery;
    if(deliveryEnsured) {
      radio.disableNoAck();
      radio.setRetries( 15,15);
    }
    else {
      radio.enableNoAck();
      radio.setRetries(0,0);
    }
  }

  void sendLegData(LegData * data, int length) {
    int base = 0;
    buffer[0] = 0;
    while(length > 0 ) {
      int loopEnd = length;
      if(length > 8)
        loopEnd = 8;
      for(int i = 0; i < loopEnd; i++) {
        LegData  item = data[i + base];
        // first 4 bits are the leg number, second are the value
        uint8_t byte1 = (item.value & 0xF);
        int pos = i/2 + 1;
        int nibble = i % 2;
        if( nibble == 0 )
          buffer[pos] = byte1;
        else 
          buffer[pos] = (byte1 << 4) | buffer[pos];
      }
      write();

      base += loopEnd;
      length -= loopEnd;
    }
#ifdef SHOW_REFRESH_RATE
    refreshCount ++;
    if(refreshCount == 100) {
      unsigned long cTime = millis();
      Serial.print((1000.0/((float)cTime - dataStartTime)) * 100.0);
      Serial.println("hz");
      refreshCount =0;
      dataStartTime = millis();
    }
#endif
    clearBuffer();

  }

  void sendPixelData(byte * cBuffer, int bitDepth, int length) {
    int base = 0;
    while(length > 0 ) {
      int loopEnd = length;
      if(length > 8)
        loopEnd = 8;
      for(int i = 0; i < loopEnd; i++) {
        int offset = i + base;
        byte r = cBuffer[offset], g = cBuffer[offset+1],b=cBuffer[offset+2];
        uint32_t c = 0;
        if(bitDepth == 16) {
          c = 
            (map(r,0,255,0,32) << 27) |
            (map(g,0,255,0,64) << 21) |
            (map(b,0,255,0,32) << 16);
        }
        else {
          c = (r << 24) | (g << 16) | (b << 8);
        }
        uint8_t byte1 = i + base;
        int pos = i*4;
        buffer[pos] = byte1;
        for(int i = 1; i < 4; i++)
          buffer[pos + i] = (c >> (32-(8*i))) & 0xFF;
      }
      write();
      base += loopEnd;
      length -= loopEnd;
    }



  }

private:


  uint8_t buffer[32];
  Mode currentMode;
  RF24 radio;
  bool ensureDelivery;
  void writeEnsured() {
    bool prevEnsure = ensureDelivery;
    setDeliveryEnsured(true);
    write();
    setDeliveryEnsured(prevEnsure);
  }
  void write() {
    radio.stopListening();

#ifdef PRINT_DEBUG
    for(int i = 0; i < radio.getPayloadSize(); i++) {
        Serial.print(buffer[i]);
        Serial.print(" ");
    }
    Serial.println();
#endif

    while(!radio.write(buffer, radio.getPayloadSize()) && ensureDelivery) {
      delay(50);
    }
    radio.startListening();
  }
  void clearBuffer() {
      memset(buffer, 0, 32);
  }

};

#endif